package com.joinway.framework.extension.groovy

import java.lang.reflect.Field

import javax.persistence.Column
import javax.persistence.Table

import org.apache.commons.lang3.StringUtils

import com.joinway.framework.bean.domain.constant.SqlConstants
import com.joinway.framework.extension.utils.DataUtils
import com.joinway.framework.extension.utils.DateTimeUtils

public final class DomainUtils {

//	private static final String MYSQL_ESCAPE_CHAR = "`"
	
	/**
	 * 
	 * @param name
	 * @return
	 */
	public static String convertToDBName(String name) {
		return convertToDBName(name, true, null);
	}
	
	/**
	 * 
	 * @param name
	 * @param dbType
	 * @return
	 */
	public static String convertToDBName(String name, String dbType) {
		return convertToDBName(name, true, dbType);
	}
	
	/**
	 * 
	 * @param name
	 * @param lcase
	 * @return
	 */
	public static String convertToDBName(String name, boolean lcase) {
		return convertToDBName(name, lcase, null);
	}
	
	/**
	 * convert a name to db table or column name format
	 * @param name
	 * @param lcase
	 * @param dbType
	 * @return
	 */
	public static String convertToDBName(String name, boolean lcase, String dbType) {
		List<Character> elements = new ArrayList<>();

		char[] arr = StringUtils.trimToEmpty(name).toCharArray();
		for (char c : arr) {
			if (Character.isUpperCase(c)) {
				elements.add('_');
			}

			elements.add(c);
		}

		char[] seq = new char[elements.size()];
		for (int i = 0; i < elements.size(); i++) {
			seq[i] = elements.get(i);
		}

		String dbName = String.valueOf(seq)
		if(dbName.startsWith('_')){
			dbName = dbName.substring(1)
		}

		dbName = lcase ? dbName.toLowerCase() : dbName.toUpperCase()
		
		if(!SqlConstants.Key.All.equals(dbName)){
			dbName = DBConverter.escapeColumnName(dbName, dbType)
		}
		
		dbName
	}

//	static String escapeToMySqlName(String name){
//		String qname = name
//		
//		boolean match = false
//		if(qname ==~ /\s*\*\s*/ 	// select *
//			|| qname ==~ /(?i)\s*count\s*\(\s*[a-zA-Z_0-9*]+\s*\)\s*/	// select count(x) 
//			|| qname ==~ /(?i)\s*sum\s*\(\s*[a-zA-Z_0-9*]+\s*\)\s*/)	// select sum(x)
//		{	
//			match = true
//		}
//		
//		if(qname && !match){
//			if(!qname.startsWith(MYSQL_ESCAPE_CHAR)){
//				qname = "`" + qname
//			}
//			if(!qname.endsWith(MYSQL_ESCAPE_CHAR)){
//				qname += "`"
//			}
//		}
//		
//		qname
//	}
	
//	static String unescapeFromMySqlName(String name){
//		String uname = name
//		
//		if(uname){
//			if(uname.startsWith(MYSQL_ESCAPE_CHAR)){
//				uname = uname.substring(1, uname.length())
//			}
//			if(uname.endsWith(MYSQL_ESCAPE_CHAR)){
//				uname = uname.substring(0, uname.length() - 1)
//			}
//		}
//		
//		uname
//	}
	
	/**
	 * domain field type
	 * @param type
	 * @param dbValue
	 * @return
	 */
	public static def convertValue(Class type, def originalValue){
		def value = originalValue
//		println "original value is $originalValue"
		switch(type){
			case String:
				value = originalValue ? String.valueOf(originalValue) : ""
				break
			case [byte]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? Byte.valueOf(String.valueOf(convertBooleanToNumber(originalValue))) : Byte.valueOf(String.valueOf(originalValue))) : Byte.valueOf("0")
				break
			case [Byte]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? Byte.valueOf(String.valueOf(convertBooleanToNumber(originalValue))) : Byte.valueOf(String.valueOf(originalValue))) : null
				break
			case [short, Short]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? convertBooleanToNumber(originalValue) : Short.valueOf(String.valueOf(originalValue))) : 0
				break
			case [int, Integer]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? convertBooleanToNumber(originalValue) : Integer.valueOf(String.valueOf(originalValue))) : 0
				break
			case [long, Long]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? convertBooleanToNumber(originalValue) : Long.valueOf(String.valueOf(originalValue))) : 0
				break
			case [float, Float]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? convertBooleanToNumber(originalValue) : Float.valueOf(String.valueOf(originalValue))) : 0
				break
			case [double, Double]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? convertBooleanToNumber(originalValue) : Double.valueOf(String.valueOf(originalValue))) : 0
				break
			case [BigInteger]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? BigInteger.valueOf(convertBooleanToNumber(originalValue)) : originalValue) : BigInteger.valueOf(0)
				break
			case [BigDecimal]:
				value = originalValue ? (originalValue.getClass() == Boolean.class ? BigDecimal.valueOf(convertBooleanToNumber(originalValue)) : originalValue) : BigDecimal.valueOf(0)
				break
			case [boolean, Boolean]:
				value = originalValue ? originalValue : false
				break
			case [char, Character]:
				value = originalValue ? String.valueOf(originalValue).charAt(0) : ' ' as char
				break
			case [Date]:
				value = originalValue ? originalValue : DateTimeUtils.convertToDate(originalValue)
		}
		
		value
	}

	public static String getTableName(Class domainClass){
		return getTableName(domainClass, null)
	}
	
	public static String getTableName(Class domainClass, String dbType){
		String tableName
		
		Table table = domainClass.getAnnotation(Table.class)
		if(table){
			tableName = DBConverter.escapeColumnName(table.name())
		}else{
			tableName = DomainUtils.convertToDBName(domainClass.getSimpleName())
		}

		tableName
	}
	
	public static String getColumnName(Field field){
		return getColumnName(field, null)
	}
	
	public static String getColumnName(Field field, String dbType){
		Column column = field.getAnnotation(Column.class)
		String columnName
		
		if(!column){
			columnName = DomainUtils.convertToDBName(field.getName())
		}else{
			if(SqlConstants.DataBase.MySql.equalsIgnoreCase(dbType)){
				columnName = DBConverter.escapeColumnName(column.name())
			}else{
				columnName = column.name()
			}
		}
		
		columnName
	}
	
	public static convertBooleanToNumber(Boolean value){
		value == true ? 1 : 0
	}
	
	public static<T> T convert(def domain, Class<T> type) throws Exception {
		T target = type.newInstance()
		
		Set<Field> fields = DataUtils.getValidFields(type)
		
		Map map = new HashMap()
		domain.getProperties().each{
			map.put(it.key, it.value)
		}
		
		fields.each {
			String fieldName = it.name
//			println fieldName
			if(map.containsKey(fieldName)){
//				println domain."$fieldName"
				target."$fieldName" = map.get(fieldName)
			}
		}
		
		target
	}
	
	public static String convertToDomainFieldName(String columnName){
		String name
		
		String cname = DBConverter.unescapeColumnName(columnName)
		
		if(cname.contains("_")){
			List list = cname.split("_")
			list.eachWithIndex { String e, i ->
				String v = e.toLowerCase()
				
				if(i == 0){
					name = v
				}else{
					name = name + StringUtils.capitalize(v)
				}
			}
		}else{
			if(StringUtils.isAllUpperCase(cname)){
				name = cname.toLowerCase()
			}else{
				name = cname
			}
		}
		
		name
	}
	
	public static List<Map<String, ?>> convertToRowMaps(List domains){
		List<Map> rows
		
		if(domains){
			rows = []
			
			def domain = domains[0]
			
			if(domain instanceof GroovyObject){
				domains.each{
					Map row = new HashMap()
					it.properties.each {
						if(!GroovyUtils.AST_FIELDS.contains(it.key)){
							row.put(it.key, it.value)
						}
					}
					rows << row
				}
			}else{
				Set<Field> fields = DataUtils.getValidFields(domain.getClass())
				domains.each{
					Map row = new HashMap()
					def d = it
					fields.each {
						String fieldName = it.getName()
						row.put(fieldName, d."$fieldName")
					}
					rows << row
				}
			}
		}
		
		rows
	}
	
	private DomainUtils() {}

}

